// {{{ includes
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/times.h>

#include <linux/user.h>
#include <asm/page.h>

#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
// }}}


// {{{ globals
#define result(x...)  { \
    struct tms t;\
    times(&t);\
    fprintf(stderr,"%ld %ld\n",t.tms_cutime*1000/sysconf(_SC_CLK_TCK),t.tms_cstime*1000/sysconf(_SC_CLK_TCK));\
    fprintf(stderr, x); \
    fprintf(stderr, "\n"); \
    if(tpid>0)\
	kill(tpid,SIGKILL);\
    exit(0); \
}

#define fatal(x...)  { \
    struct tms t;\
    times(&t);\
    fprintf(stderr,"%ld %ld\n",t.tms_cutime*1000/sysconf(_SC_CLK_TCK),t.tms_cstime*1000/sysconf(_SC_CLK_TCK));\
    fprintf(stderr, x); \
    fprintf(stderr, "\n"); \
    destroy_child(); \
    exit(1); \
}

const char *VERSION = "1.5";

// {
// {{ syscalls
const char *syscall_names[] = {
    "restart-syscall",        "exit",                   "fork",
    "read",                   "write",                  "open",
    "close",                  "waitpid",                "creat",
    "link",                   "unlink",                 "execve",
    "chdir",                  "time",                   "mknod",
    "chmod",                  "lchown",                 "break",
    "oldstat",                "lseek",                  "getpid",
    "mount",                  "umount",                 "setuid",
    "getuid",                 "stime",                  "ptrace",
    "alarm",                  "oldfstab",               "pause",
    "utime",                  "stty",                   "gtty",
    "access",                 "nice",                   "ftime",
    "sync",                   "kill",                   "rename",
    "mkdir",                  "rmdir",                  "dup",
    "pipe",                   "times",                  "prof",
    "brk",                    "setgid",                 "getgid",
    "signal",                 "geteuid",                "getegid",
    "acct",                   "umount2",                "lock",
    "ioctl",                  "fcntl",                  "mpx",
    "setpgid",                "ulimit",                 "oldolduname",
    "umask",                  "chroot",                 "ustat",
    "dup2",                   "setppid",                "getpgrp",
    "setsid",                 "sigaction",              "sgetmask",
    "ssetmask",               "setreuid",               "setregid",
    "sigsuspend",             "sigpending",             "sethostname",
    "setrlimit",              "getrlimit",              "getrusage",
    "gettimeofday",           "settimeofday",           "getgroups",
    "setgroups",              "select",                 "symlink",
    "oldlstat",               "readlink",               "uselib",
    "swapon",                 "reboot",                 "readdir",
    "mmap",                   "munmap",                 "truncate",
    "ftruncate",              "fchmod",                 "fchown",
    "getpriority",            "setpriority",            "profil",
    "statfs",                 "fstatfs",                "ioperm",
    "socketcall",             "syslog",                 "setitimer",
    "getitimer",              "stat",                   "lstat",
    "fstat",                  "olduname",               "iopl",
    "vhangup",                "idle",                   "vm86old",
    "wait4",                  "swapoff",                "sysinfo",
    "ipc",                    "fsync",                  "sigreturn",
    "clone",                  "setdomainname",          "uname",
    "modify_ldt",             "adjtimex",               "mprotect",
    "sigprocmask",            "create_module",          "init_module",
    "delete_module",          "get_kernel_syms",        "quotactl",
    "getpgid",                "fchdir",                 "bdflush",
    "sysfs",                  "personality",            "afs_syscall",
    "setfsuid",               "setfsgid",               "_llseek",
    "getdents",               "_newselect",             "flock",
    "msync",                  "readv",                  "writev",
    "getsid",                 "fdatasync",              "_sysctl",
    "mlock",                  "munlock",                "mlockall",
    "munlockall",             "sched_setparam",         "sched_getparam",
    "sched_setscheduler",     "sched_getscheduler",     "sched_yield",
    "sched_get_priority_max", "sched_get_priority_min", "sched_rr_get_interval",
    "nanosleep",              "mremap",                 "setresuid",
    "getresuid",              "vm86",                   "query_module",
    "poll",                   "nfsservctl",             "setresgid",
    "getresgid",              "prctl",                  "rt_sigreturn",
    "rt_sigaction",           "rt_sigprocmask",         "rt_sigpending",
    "rt_sigtimedwait",        "rt_sigqueueinfo",        "rt_sigsuspend",
    "pread64",                "pwrite64",               "chown",
    "getcwd",                 "capget",                 "capset",
    "sigaltstack",            "sendfile",               "getpmsg",
    "putpmsg",                "vfork",                  "ugetrlimit",
    "mmap2",                  "truncate64",             "ftruncate64",
    "stat64",                 "lstat64",                "fstat64",
    "lchown32",               "getuid32",               "getgid32",
    "geteuid32",              "getegid32",              "setreuid32",
    "setregid32",             "getgroups32",            "setgroups32",
    "fchown32",               "setresuid32",            "getresuid32",
    "setresgid32",            "getresgid32",            "chown32",
    "setuid32",               "setgid32",               "setfsuid32",
    "setfsgid32",             "pivot_root",             "mincore",
    "madvise",                "getdents64",             "fcntl64",
    "ni_syscall",             "ni_syscall",             "gettid",
    "readahead",              "setxattr",               "lsetxattr",
    "fsetxattr",              "getxattr",               "lgetxattr",
    "fgetxattr",              "listxattr",              "llistxattr",
    "flistxattr",             "removexattr",            "lremovexattr",
    "fremovexattr",           "tkill",                  "sendfile64",
    "futex",                  "sched_setaffinity",      "sched_getaffinity",
    "set_thread_area",        "get_thread_area",        "io_setup",
    "io_destroy",             "io_getevents",           "io_submit",
    "io_cancel",              "fadvise64",              "ni_syscall",
    "exit_group",             "lookup_dcookie",         "epoll_create",
    "epoll_ctl",              "epoll_wait",             "remap_file_pages",
    "set_tid_address",        "timer_create",           "timer_settime",
    "timer_gettime",          "timer_getoverrun",       "timer_delete",
    "clock_settime",          "clock_gettime",          "clock_getres",
    "clock_nanosleep",        "statfs64",               "fstatfs64",
    "tgkill",                 "utimes",                 "fadvise64_64",
    "vserver",                "mbind",                  "get_mempolicy",
    "set_mempolicy",          "mq_open",                "mq_unlink",
    "mq_timedsend",           "mq_timedreceive",        "mq_notify",
    "mq_getsetattr",          "sys_kexec_load",         "waitid",
    "ni_syscall",             "add_key",                "request_key",
    "keyctl",                 "ioprio_set",             "ioprio_get",
    "inotify_init",           "inotift_add_watch",      "inotift_rm_watch"
};
int syscall_names_count = 294;
// }}}

int tpid;

void trace_loop();
void dump(struct user_regs_struct *r);
void check_syscall(struct user_regs_struct *r);
void handle_syscall();
void destroy_child();
void sighandler(int signal);
//unsigned long long int get_cputime();
//unsigned long int get_memsize();
char *get_string(int ptr);
void setup_sighandlers();
void setup_child(char **argv);
void usage();
int main(int argc, char **argv);
// }}}
// 


// {{{ trace_loop()
void trace_loop()
{
    struct user_regs_struct regs;

    int called = 0;
    int status;

    while (1) {
        if (ptrace(PTRACE_SYSCALL, tpid, 0, 0)) {
            fatal("ERR:PTRACE_SYSCALL failed");
        }

        if (waitpid(tpid, &status, WUNTRACED) < 0) {
            fatal("ERR:waitpid failed");
        }

        if (WIFEXITED(status)) {
	    if(WEXITSTATUS(status)==0)
		    result("ACC:")
	    else
		    result("RCD: %d",WEXITSTATUS(status));
        }

        if (WIFSIGNALED(status)) {
            result("SIG:signaled %d", WTERMSIG(status));
        }

        if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
            if (called) {
                called = 0;
            } else {
                if (ptrace(PTRACE_GETREGS, tpid, 0, &regs)) {
                    fatal("ERR:PTRACE_GETREGS failed");
                }
                check_syscall(&regs);
                called = 1;
            }

            continue;
        }

        if (WIFSTOPPED(status)) {
            // everything but SIGTRAP handled above
            result("SIG:stopped with signal: %d", WSTOPSIG(status));
        }

        fatal("ERR:unknown waitpid() status: %d", status);
    }

}


// 
// }}}

// {{{ dump()
void dump(struct user_regs_struct *r)
{
    static int header_printed = 0;

    if (!header_printed) {
        fprintf(stderr, "NUM       NAME              "
            "EBX           ECX           EDX       ADD\n");
        header_printed = 1;
    }

    if (r->orig_eax < 0 || r->orig_eax >= syscall_names_count) {
        fatal("ERR:cannot dump syscall: %ld", r->orig_eax);
    }

    fprintf(stderr, "%-4ld %-15s  %12lu  %12lu  %12lu", r->orig_eax,
        syscall_names[r->orig_eax], r->ebx, r->ecx, r->edx);

    if (r->orig_eax == 5) { // open
        fprintf(stderr, "  %s", get_string(r->ebx));
    }

    fprintf(stderr, "\n");
}

// }}}


// {{{ check_syscall()
void check_syscall(struct user_regs_struct *r)
{
    switch (r->orig_eax) {
    // allowed:
        case 1:   // exit
        case 3:   // read
        case 6:   // close
        case 13:  // time
        case 33:  // access
        case 36:  // sync
        case 45:  // brk
        case 85:  // readlink
        case 90:  // mmap
        case 91:  // munmap
        case 106: // stat
        case 107: // lstat
        case 108: // fstat
        case 122: // uname
        case 146: // writev
        case 125: // mprotect
        case 132: // getpgid
        case 141: // getdents
        case 143: // flock
        case 174: // rt_sigaction
        case 192: // mmap2
        case 197: // fstat64
        case 219: // madvise
        case 243: // set_thread_area
        case 252: // exit_group
            break;

    // allowed conditionally:
        case 4:   // write
            if (r->ebx == 1) {
                break;
            };

            goto forbid;

        case 26:  // ptrace
            if (r->ebx == PTRACE_ATTACH) {
                break;
            };

            goto forbid;

        case 54:  // ioctl
            {
                int allowed = 0;

                switch (r->ecx) {

                case 0x00005401:  // TCGETS (terminal)
                case 0x00005405:  // TCGETA (terminal)
                case 0x0000540F:  // TIOCGPGRP (terminal I/O)
                case 0x00005410:  // TIOCSPGRP (terminal I/O)
                case 0x00005413:  // TIOCGWINSZ (terminal I/O)
                case 0x00005414:  // TIOCSWINSZ (terminal I/O)
                case 0x0000541B:  // FIONREAD / TIOCINQ (I/O)
                case 0x0000541D:  // TIOCCONS (console)
                case 0x00005421:  // FIONBIO (I/O)
                case 0x00005424:  // TIOCGETD (terminal I/O)
                case 0x00005450:  // FIONCLEX (I/O)
                case 0x00005451:  // FIOCLEX (I/O)
                case 0x00005452:  // FIOASYNC (I/O)
                case 0x00008901:  // FIOSETOWN (I/O)
                case 0x00008903:  // FIOGETOWN (I/O)

                    allowed = 1;
                }

                if (allowed) {
                    break;
                }

                goto forbid;
            }

    // forbidden:
        case 2:   // fork
        case 5:   // open
        case 7:   // waitpid
        case 8:   // creat
        case 9:   // link
        case 10:  // unlink
        case 11:  // execve
        case 12:  // chdir
        case 14:  // mknod
        case 15:  // chmod
        case 16:  // lchown
        case 17:  // break
        case 18:  // oldstat
        case 19:  // lseek
        case 20:  // getpid
        case 21:  // mount
        case 22:  // umount
        case 23:  // setuid
        case 24:  // getuid
        case 25:  // stime
        case 27:  // alarm
        case 28:  // oldfstab
        case 29:  // pause
        case 30:  // utime
        case 31:  // stty
        case 32:  // gtty
        case 34:  // nice
        case 35:  // ftime
        case 37:  // kill
        case 38:  // rename
        case 39:  // mkdir
        case 40:  // rmdir
        case 41:  // dup
        case 42:  // pipe
        case 43:  // times
        case 44:  // prof
        case 46:  // setgid
        case 47:  // getgid
        case 48:  // signal
        case 49:  // geteuid
        case 50:  // getegid
        case 51:  // acct
        case 52:  // umount2
        case 53:  // lock
        case 55:  // fcntl
        case 56:  // mpx
        case 57:  // setpgid
        case 58:  // ulimit
        case 59:  // oldolduname
        case 60:  // umask
        case 61:  // chroot
        case 62:  // ustat
        case 63:  // dup2
        case 64:  // setppid
        case 65:  // getpgrp
        case 66:  // setsid
        case 67:  // sigaction
        case 68:  // sgetmask
        case 69:  // ssetmask
        case 70:  // setreuid
        case 71:  // setregid
        case 72:  // sigsuspend
        case 73:  // sigpending
        case 74:  // sethostname
        case 75:  // setrlimit
        case 76:  // getrlimit
        case 77:  // getrusage
        case 78:  // gettimeofday
        case 79:  // settimeofday
        case 80:  // getgroups
        case 81:  // setgroups
        case 82:  // select
        case 83:  // symlink
        case 84:  // oldlstat
        case 86:  // uselib
        case 87:  // swapon
        case 88:  // reboot
        case 89:  // readdir
        case 92:  // truncate
        case 93:  // ftruncate
        case 94:  // fchmod
        case 95:  // fchown
        case 96:  // getpriority
        case 97:  // setpriority
        case 98:  // profil
        case 99:  // statfs
        case 100: // fstatfs
        case 101: // ioperm
        case 102: // socketcall
        case 103: // syslog
        case 104: // setitimer
        case 105: // getitimer
        case 109: // olduname
        case 110: // iopl
        case 111: // vhangup
        case 112: // idle
        case 113: // vm86old
        case 114: // wait4
        case 115: // swapoff
        case 116: // sysinfo
        case 117: // ipc
        case 118: // fsync
        case 119: // sigreturn
        case 120: // clone
        case 121: // setdomainname
        case 123: // modify_ldt
        case 124: // adjtimex
        case 126: // sigprocmask
        case 127: // create_module
        case 128: // init_module
        case 129: // delete_module
        case 130: // get_kernel_syms
        case 131: // quotactl
        case 133: // fchdir
        case 134: // bdflush
        case 135: // sysfs
        case 136: // personality
        case 137: // afs_syscall
        case 138: // setfsuid
        case 139: // setfsgid
        case 140: // _llseek
        case 142: // _newselect
        case 144: // msync
        case 145: // readv
        case 147: // getsid
        case 148: // fdatasync
        case 149: // _sysctl
        case 150: // mlock
        case 151: // munlock
        case 152: // mlockall
        case 153: // munlockall
        case 154: // sched_setparam
        case 155: // sched_getparam
        case 156: // sched_setscheduler
        case 157: // sched_getscheduler
        case 158: // sched_yield
        case 159: // sched_get_priority_max
        case 160: // sched_get_priority_min
        case 161: // sched_rr_get_interval
        case 162: // nanosleep
        case 163: // mremap
        case 164: // setresuid
        case 165: // getresuid
        case 166: // vm86
        case 167: // query_module
        case 168: // poll
        case 169: // nfsservctl
        case 170: // setresgid
        case 171: // getresgid
        case 172: // prctl
        case 173: // rt_sigreturn
        case 175: // rt_sigprocmask
        case 176: // rt_sigpending
        case 177: // rt_sigtimedwait
        case 178: // rt_sigqueueinfo
        case 179: // rt_sigsuspend
        case 180: // pread64
        case 181: // pwrite64
        case 182: // chown
        case 183: // getcwd
        case 184: // capget
        case 185: // capset
        case 186: // sigaltstack
        case 187: // sendfile
        case 188: // getpmsg
        case 189: // putpmsg
        case 190: // vfork
        case 191: // ugetrlimit
        case 193: // truncate64
        case 194: // ftruncate64
        case 195: // stat64
        case 196: // lstat64
        case 198: // lchown32
        case 199: // getuid32
        case 200: // getgid32
        case 201: // geteuid32
        case 202: // getegid32
        case 203: // setreuid32
        case 204: // setregid32
        case 205: // getgroups32
        case 206: // setgroups32
        case 207: // fchown32
        case 208: // setresuid32
        case 209: // getresuid32
        case 210: // setresgid32
        case 211: // getresgid32
        case 212: // chown32
        case 213: // setuid32
        case 214: // setgid32
        case 215: // setfsuid32
        case 216: // setfsgid32
        case 217: // pivot_root
        case 218: // mincore
        case 220: // getdents64
        case 221: // fcntl64
        case 222: // ni_syscall
        case 223: // ni_syscall
        case 224: // gettid
        case 225: // readahead
        case 226: // setxattr
        case 227: // lsetxattr
        case 228: // fsetxattr
        case 229: // getxattr
        case 230: // lgetxattr
        case 231: // fgetxattr
        case 232: // listxattr
        case 233: // llistxattr
        case 234: // flistxattr
        case 235: // removexattr
        case 236: // lremovexattr
        case 237: // fremovexattr
        case 238: // tkill
        case 239: // sendfile64
        case 240: // futex
        case 241: // sched_setaffinity
        case 242: // sched_getaffinity
        case 244: // get_thread_area
        case 245: // io_setup
        case 246: // io_destroy
        case 247: // io_getevents
        case 248: // io_submit
        case 249: // io_cancel
        case 250: // fadvise64
        case 251: // ni_syscall
        case 253: // lookup_dcookie
        case 254: // epoll_create
        case 255: // epoll_ctl
        case 256: // epoll_wait
        case 257: // remap_file_pages
        case 258: // set_tid_address
        case 259: // timer_create
        case 260: // timer_settime
        case 261: // timer_gettime
        case 262: // timer_getoverrun
        case 263: // timer_delete
        case 264: // clock_settime
        case 265: // clock_gettime
        case 266: // clock_getres
        case 267: // clock_nanosleep
        case 268: // statfs64
        case 269: // fstatfs64
        case 270: // tgkill
        case 271: // utimes
        case 272: // fadvise64_64
        case 273: // vserver
        case 274: // mbind
        case 275: // get_mempolicy
        case 276: // set_mempolicy
        case 277: // mq_open
        case 278: // mq_unlink
        case 279: // mq_timedsend
        case 280: // mq_timedreceive
        case 281: // mq_notify
        case 282: // mq_getsetattr
        case 283: // sys_kexec_load
        case 284: // waitid
        case 285: // ni_syscall
        case 286: // add_key
        case 287: // request_key
        case 288: // keyctl
        case 289: // ioprio_set
        case 290: // ioprio_get
        case 291: // inotify_init
        case 292: // inotift_add_watch
        case 293: // inotift_rm_watch

        default:
        forbid:

//            dump(r);
            result("SCL:E_SYSCALL(%s): syscall not allowed",syscall_names[r->orig_eax]);

    }
    #ifdef DEBUG
    dump(r);
    #endif
}
// }}}

//
//{{{ destroy_child()
void destroy_child()
{
    static struct user_regs_struct r;

    if (tpid > 0) {
        ptrace(PTRACE_SETREGS, tpid, 0, &r);
        kill(tpid, SIGKILL);
    }
}
// }}}

//
//{{{ sighandler()
void sighandler(int signal)
{
    fatal("ERR:caught signal %d", signal);
}
// }}}

// {{{ get_string()
// 
char *get_string(int ptr) {
    static char gsret[8192 + 8];
    int *wtab = (int*) gsret;
    int cur = 0;

    while (cur < 2048) {
        errno = 0;
        wtab[cur] = ptrace(PTRACE_PEEKDATA, tpid, ptr, 0);
        if (errno) {
            wtab[cur] = 0;
            return gsret;
        }

        if (!(gsret[cur*4] && gsret[cur*4+1] && gsret[cur*4+2] && gsret[cur*4+3])) {
            return gsret;
        }

        cur++;
        ptr += 4;
    }

    wtab[cur] = 0;
    return gsret;
}
// }}}

// 
// {{{ setup_sighandlers()
void setup_sighandlers()
{
    signal(SIGTERM, sighandler);
    signal(SIGHUP, sighandler);
    signal(SIGINT, sighandler);
    signal(SIGQUIT, sighandler);
    signal(SIGABRT, sighandler);
    signal(SIGBUS, sighandler);
    signal(SIGSEGV, sighandler);
}
// }}}

// {{{ setup_child()
void setup_child(char **argv)
{
    int status;

    tpid = fork();

    if (tpid < 0) {
        fatal("ERR:fork() failed");
    }

    if (tpid == 0) {    // child
        if (ptrace(PTRACE_TRACEME, getppid(), 0, 0)) {
            fatal("ERR:ptrace() failed");
        }

        execvp(argv[0], argv);
        fatal("ERR:execvp() failed");
    } else {            // parent
        if (waitpid(tpid, &status, WUNTRACED) < 0 || !WIFSTOPPED(status))
        {
            fatal("ERR:error executing program");
        }
    }
}
// 

// }}}


// {{{ usage()
void usage()
{
    extern char *__progname;
    fprintf(stderr,
        "Usage: %s [-m size] [-t time] -- command [arg ...]\n"
        "Run command in strictly controlled environment\n\n"
        "-m size -- memory limit in bytes\n"
        "-t time -- cpu time limit in miliseconds\n", __progname);
    exit(1);
}
// }}}
// 


// {{{ main()
int main(int argc,char** argv)
{
    int ch;
    char *ep;

    long int mmax = 0;
    long int tmax = 0;
    struct rlimit limit;


    while ((ch = getopt(argc, argv, "hm:t:v")) != -1) {
        switch (ch) {
        case 'm':
            errno = 0;
            mmax = strtol(optarg, &ep, 10);
	    mmax *= 1024;
	    mmax += 2048; //for saferun
            if (mmax <= 0 || *ep != '\0' || (errno == ERANGE &&
                (mmax == LONG_MAX || mmax == LONG_MIN)) )
                fatal("ERR:illegal mem value: %s", optarg);
            limit.rlim_cur = mmax;
            limit.rlim_max = mmax;
            setrlimit(RLIMIT_AS, &limit);

            break;

        case 't':
            errno = 0;
            tmax = strtol(optarg, &ep, 10);
	    tmax /= 1000;
	    tmax+=1;
            if (tmax <= 0 || *ep != '\0' || (errno == ERANGE &&
                (tmax == LONG_MAX || tmax == LONG_MIN)) )
                fatal("ERR:illegal time value: %s", optarg);
            limit.rlim_cur = tmax;
            limit.rlim_max = tmax;
            setrlimit(RLIMIT_CPU, &limit);

            break;

        case 'v':
            printf("sr %s\n", VERSION);
            exit(0);

            break;

        case 'h':
        default:
            usage();
        }
    }
    argc -= optind;
    argv += optind;

    if (argc < 1) {
        usage();
    }

    setup_sighandlers();
    setup_child(argv);
//    if (mmax || tmax) {
//        setup_limiter(mmax, tmax);
//    }

    trace_loop();

    return 0;
}
// 
// }}}

